Skip to content

MemoryCache 在 ASP.NET MVC 上的應用

TLDR

  • OutputCacheAttribute 是 MVC 內建的快取機制,預設使用 MemoryCache 實作。
  • 若需針對不同使用者或權限進行快取,需透過 Global.asax.csGetVaryByCustomString 實作自訂快取 Key。
  • NoStoreLocation.None 的行為不同:NoStore 僅影響瀏覽器快取,Location.None 則同時停用伺服器與瀏覽器快取。
  • 若需確保資料庫異動時快取能即時更新,可使用 SqlChangeMonitor 結合 SqlDependency 監聽資料庫變更。
  • 使用 SqlDependency 前,必須在 SQL Server 資料庫中啟用 Service Broker 功能。

使用 ActionFilter 快取 Action 內容

OutputCacheAttribute 是 ASP.NET MVC 提供的 ActionFilter,用於標註 Action Method 的快取行為。

核心屬性說明

  • Duration: 快取持續時間(秒)。
  • Location: 指定快取儲存位置(如 Server, Client, Any 等)。
  • VaryByParam: 根據參數(如 QueryString 或 POST 參數)區分快取內容。
  • CacheProfile: 引用 Web.config 中預定義的快取設定,便於統一管理。

TIP

NoStoreLocation.None 的差異:

  • NoStore: 將 Cache-Control 設為 no-store,僅告知瀏覽器不要快取,不影響 Web Server 的快取行為。
  • Location.None: 將 Cache-Control 設為 no-cache,並強制不儲存 Web Server 的快取。

針對不同使用者進行快取

當應用程式需要根據使用者權限或 Cookie 區分快取內容時,單純使用 VaryByParam 會導致快取衝突。此時需在 Global.asax.cs 中覆寫 GetVaryByCustomString 方法。

什麼情況下會遇到這個問題:當網站具有使用者登入機制,且不同權限的使用者存取相同 URL 時,若未區分快取 Key,會導致使用者看到他人的快取資料。

實作範例

Global.asax.cs 中定義自訂邏輯:

csharp
public override string GetVaryByCustomString(HttpContext context, string custom) {
    const string OutputCacheKey = "OutputCacheId";

    if (custom.Equals("Cookie", StringComparison.OrdinalIgnoreCase)) {
        if (Request.Cookies[OutputCacheKey] == null) {
            string cacheId = Guid.NewGuid().ToString();
            Response.Cookies.Add(new HttpCookie(OutputCacheKey) {
                Value = cacheId,
                HttpOnly = true,
                Expires = DateTime.Now.AddHours(1)
            });
            return cacheId;
        }
        return Request.Cookies[OutputCacheKey].Value;
    }
    return base.GetVaryByCustomString(context, custom);
}

更新資料庫時清除快取資料

為了避免資料庫異動後,快取仍保留舊資料,可以使用 SqlChangeMonitor 監聽 SQL Server 的變更。

什麼情況下會遇到這個問題:當快取資料來源於資料庫,且該資料庫資料會被其他外部程式或直接透過 SQL 指令修改,導致應用程式無法透過 API 主動清除快取時。

實作機制

SqlChangeMonitor 透過 SqlDependency 監聽 SQL Server 的通知。當資料異動時,SqlDependency 會觸發事件,進而通知 MemoryCache 清除對應的快取項目。

實作步驟

  1. 啟用 Service Broker: 必須在資料庫執行 ALTER DATABASE {資料庫名稱} SET ENABLE_BROKER;
  2. 初始化監聽: 在 Global.asax.csApplication_Start 呼叫 SqlDependency.Start()
  3. 設定監控: 在建立快取時,將 SqlChangeMonitor 加入 CacheItemPolicy

監聽範例

csharp
private void CreateCache() {
    string connectionStr = WebConfigurationManager.ConnectionStrings["MyDB"].ConnectionString;
    CacheItemPolicy policy = new CacheItemPolicy();
    
    using (SqlConnection conn = new SqlConnection(connectionStr))
    using (SqlCommand cmd = new SqlCommand("SELECT Key1 FROM dbo.Config", conn)) {
        SqlDependency dependency = new SqlDependency(cmd);
        dependency.OnChange += SqlDependencyOnChange;

        conn.Open();
        string key1 = cmd.ExecuteScalar().ToString();

        SqlChangeMonitor monitor = new SqlChangeMonitor(dependency);
        policy.ChangeMonitors.Add(monitor);

        MemoryCache.Default.Set(CacheKey, key1, policy);
    }
}

WARNING

  • 監聽的 SQL 語法必須包含 Schema(例如 dbo.TableName),且必須指定具體欄位。
  • SqlDependency 設定後,必須執行一次 SqlCommand 監聽才會生效。
  • 若資料庫曾卸載重掛,啟用 Service Broker 可能需使用 ALTER DATABASE {資料庫名稱} SET NEW_BROKER WITH ROLLBACK IMMEDIATE;

異動歷程

    • 初版文件建立。